/*========================================================================
#   FileName: linuxServer.c
#     Author: lwd
#      Email: lwd_xinghuo@126.com
#   HomePage: 
# LastChange: 2013-04-26 14:53:19
========================================================================*/
#include <netinet/in.h>
#include <sys/types.h>    // for socket
#include <sys/socket.h>   // for socket
#include <stdio.h>       // for printf
#include <stdlib.h>       // for exit
#include <string.h>       // for bzero
#include <pthread.h>
#include <unistd.h>
#include "Process.h"
#include "manage.h"
#include "socketfunc.h"
#include "config.h"

#define SERVER_PORT				5214
#define LENGTH_OF_LISTEN_QUEUE  20
#define BUFFERSIZE 				1023
#define FILENAMESIZE			(255+1)

#define PRINT_FUNC() \
	do{\
		printf("FILE:%s LINE:%d FUNC:%s\n", __FILE__, __LINE__, __func__);\
	}while(0)

#define GETFILESIZE(fp, size) \
	do{\
		int tsize = ftell(fp);\
		fseek(fp, 0, SEEK_END);\
		size = ftell(fp);\
		fseek(fp, tsize, SEEK_SET);\
	}while(0)

int getLocalPath(char* fileName, char* path)
{
	char* directory = "./data/";
	strncpy(path, directory, strlen(directory));
	strcat(path, fileName);
}

static void* handleRequest(void* arg)
{
	//PRINT_FUNC();
	//need not care this pthread
	//set it detach
	pthread_detach(pthread_self());
	
	HandleRequest *Request[MODEL_NUM] = {\
							handleReqNewJobMsg,\
							handleReqTestModelMsg,\
							handleReqParamFileMsg,\
							handleReqQueryRateMsg,\
							handleReqDelJobMsg,\
							handleReqJobRet\
	};
	int client_socket = (int)arg;
	queryRateMsg msg;
	
	int ret = recv(client_socket, (queryRateMsg *)&msg, sizeof(msg), 0);
	if(ret > 0)
	{
		Request[msg.type](client_socket, (void *)&msg);
	}
	close(client_socket);
	pthread_exit(0);
}

void *serverStartup(void* args)
{
	PRINT_FUNC();
	//create a server socket
	//int server_socket = createServerSocket();
	struct sockaddr_in server_addr;
	bzero(&server_addr, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htons(INADDR_ANY);
	server_addr.sin_port = htons(SERVER_PORT);
	
	int server_socket = socket(PF_INET, SOCK_STREAM, 0);
	if(server_socket < 0)
	{
		printf("ERROR! LINE:%d\n", __LINE__);
		exit(0);
	}
	
	if(bind(server_socket, (struct sockaddr *)&server_addr,sizeof(server_addr)))
	{
		printf("ERROR! LINE:%d\n", __LINE__);
		exit(0);
	}
	
	if(listen(server_socket, LENGTH_OF_LISTEN_QUEUE))
	{
		printf("ERROR! LINE:%d\n", __LINE__);
		exit(0);
	}
	
	
	struct sockaddr_in saclient;
	int client_socket;
	int len = sizeof(saclient);
	
	printf("waiting for client...\n");
	
	while(1)
	{
		client_socket = accept(server_socket, (struct sockaddr*)&saclient,\
			   	&len);
		//printf("accept!\n");
		if(client_socket < 0)
		{
			continue;
		}
		else
		{
			//create a new thread to handle this request
			pthread_t thread;
			pthread_create(&thread, NULL, handleRequest, (void*)client_socket);
			//printf("clientsocket = %d pthread = %ld\n", client_socket, thread);
		}
	}
	return 0;
}

static int createProcess(char* modename, char* filename1, char* filename2,\
	   	int jid)
{
	int ret = fork();
	//printf("modemame = %s pid = %d\n", modename, getppid());
	if(ret == 0)
	{
		int pid = getpid();
		printf("pid = %d\n", pid);
		char tfilename[16];
		bzero(tfilename, 16);
		int i = 0;
		for(i = 0; i < 15; i++)
		{
			tfilename[i] = '0' + jid%10;
			jid /= 10;
		}
		printf("create a child process!\n");
		//文件存放在服务程序上一级目录的data目录下
		char dataFileName[MAXFILENAMESIZE] = "./data/";
		char paraFileName[MAXFILENAMESIZE] = "./data/";
		char tempFileName[MAXFILENAMESIZE] = "./temp/";
		char rsltFileName[MAXFILENAMESIZE] = "./result/";
		strcat(dataFileName, filename1);
		strcat(paraFileName, filename2);
		strcat(tempFileName, tfilename);
		strcat(rsltFileName, tfilename);
		//getLocalPath(filename1, dataFileName);
		//getLocalPath(filename2, paraFileName);
		const Service* s = getServiceInfo(modename);
		char* args[] = { modename, paraFileName, dataFileName,\
									tempFileName, rsltFileName, NULL};
		//printf("path:%s\n", s->mpath);
		execv(s->mpath, args);
	}
	return ret;
}

int handleReqNewJobMsg(int socket, void *msg)
{
	creatNewJobMsg *cmsg = (creatNewJobMsg *)msg;
	//recv file
	recvFile(socket);
		//execv("./src/func1", args);
	recvFile(socket);
	//create child process
	Process p;
	bzero(&p, sizeof(Process));
	int cpid = createProcess(cmsg->modeName, cmsg->dataFile, cmsg->paraFile,\
			cmsg->jobID);
	printf("cpid = %d pid = %d\n", cpid, getpid());
	if(cpid > 0)
	{
		printf("cpid = %d\n", cpid);
		p.pid = cpid;
		p.jid = cmsg->jobID;
		p.state = WAIT;
		p.rate = 0;
		strncpy(p.rsltfile, cmsg->rsltFile, strlen(cmsg->rsltFile));
		sleep(1);
		add_new(p);
	}
	else
	{
		printf("create child-process failed! func:%s line:%d\n",\
				__func__, __LINE__);
	}
	
	//success
	char result[2][8] = { "success", "failed" };
	send(socket, (char*)result[0], 8, 0);
	
	return 0;
}

int handleReqParamFileMsg(int socket, void *msg)
{
	/* 如何做请求参数文件部分，待定 */
	reqParamMsg *rmsg = (reqParamMsg *)msg;
	printf("send para file to client!\n");
	//get the info of request mode
	const Service* s = getServiceInfo(rmsg->modeName);
	//send param file to client
	char buff[BUFFERSIZE+1];
	bzero(buff, BUFFERSIZE+1);
	FILE *fp;
	if((fp = fopen(s->ppath, "r")) == NULL)
	{
		fprintf(stderr, "cannot open file:%s\n", s->ppath);
		return 1;
	}
	int len = 0;
	
	while((len = fread(buff, sizeof(char), BUFFERSIZE, fp)) > 0)
	{
		printf("len = %d \n", len);
		if(send(socket, buff, len, 0) < 0)
		{
			perror("send file failed!\n");
			break;
		}
		bzero(buff, BUFFERSIZE+1);
	}
	
	return 0;
}

int handleReqQueryRateMsg(int socket, void *msg)
{
	PRINT_FUNC();
	queryRateMsg *qmsg = (queryRateMsg *)msg;
	printf("type = %d num = %d\n", qmsg->type, qmsg->num);
	int i = 0;
	Process p;
	for(i = 0; i < qmsg->num; i++)
	{
		p.jid = qmsg->jobID[i];
		if(search(&p))
		{
			qmsg->jobID[i] = -1;
		}
		else
		{
			qmsg->jobID[i] = p.rate;
		}
		printf("jobID = %d rate = %d\n", p.jid, qmsg->jobID[i]);
	}
	if(send(socket, (char*)qmsg, sizeof(queryRateMsg), 0))
	{
		return 0;
	}
	return 1;
}

int handleReqTestModelMsg(int socket, void *msg)
{
	/* 如何做测试模型部分，待定 */
	testModelMsg *tmsg = (testModelMsg *)msg;
	//recv file
	int ret = recvFile(socket);
	if(ret)
	{
		return ret;
	}
	ret = recvFile(socket);
	if(ret)
	{
		return ret;
	}
	
	char tfilename[16];
	bzero(tfilename, 16);
	unsigned long pthread_id = pthread_self();
	int i = 0;
	for(i = 0; i < 15;i++)
	{
		//使用进程号的逆序作为文件名
		tfilename[i] = pthread_id%10 + '0';
		pthread_id /= 10;
	}
	//create child process
	ret = testModel(tmsg->modeName, tmsg->modeFile, tmsg->dataFile, \
									tmsg->steplen, tfilename);
	if(ret)
	{
		printf("testModel error!\n");
		return 1;
	}
	//测试任务到优先级最高，执行之后，会在很短时间内有结果
	//可以马上返回
	//send param file to client
	char fileName[MAXFILENAMESIZE] = "./result/";
	strcat(fileName, tfilename);
	printf("fileName:%s\n", fileName);
	
	char buff[BUFFERSIZE+1];
	bzero(buff, BUFFERSIZE+1);
	FILE *fp;
	if((fp = fopen( fileName, "r")) == NULL)
	{
		fprintf(stderr, "cannot open file:%s\n", fileName);
		return 2;
	}
	int len = 0;
	while((len = fread(buff, sizeof(char), BUFFERSIZE, fp)) > 0)
	{
		printf("len = %d \n", len);
		if(send(socket, buff, len, 0) < 0)
		{
			perror("send file failed!\n");
			break;
		}
		bzero(buff, BUFFERSIZE+1);
	}
	return 0;
}

int handleReqDelJobMsg(int socket, void *msg)
{
	reqDelJobMsg *dmsg = (reqDelJobMsg*)msg;
	printf("type = %d num = %d", dmsg->type, dmsg->num);
	int i = 0;
	int ret;
	for(i = 0; i < dmsg->num; i++)
	{
		ret = killProcess(dmsg->jobIDs[i]);
		dmsg->jobIDs[i] = ret;
	}
	if(send(socket, (char*)dmsg, sizeof(reqDelJobMsg), 0))
	{
		return 0;
	}
	
	return 0;
}

int handleReqJobRet(int socket, void *msg)
{
	PRINT_FUNC();
	reqJobRet *jmsg = (reqJobRet *)msg;
	printf("send temp file to client!jid = %d\n", jmsg->jobID);
	//get the info of request process
	Process p;
	p.jid = jmsg->jobID;
	search(&p);
	int jid = p.jid;
	//send param file to client
	char fileName[MAXFILENAMESIZE];
	bzero(fileName, MAXFILENAMESIZE);
	strncpy(fileName, "./temp/", 7);
	
	char tfileName[16];
	bzero(tfileName, 16);
	int i = 0;
	for(i = 0; i < 15 ; i++)
	{
		//使用进程号的逆序作为文件名
		tfileName[i] = jid%10 + '0';
		jid /= 10;
	}
	strcat(fileName, tfileName);
	char buff[BUFFERSIZE+1];
	bzero(buff, BUFFERSIZE+1);
	FILE *fp;
	if((fp = fopen( fileName, "r")) == NULL)
	{
		fprintf(stderr, "cannot open file:%s\n", fileName);
		return 1;
	}
	int len = 0;
	
	while((len = fread(buff, sizeof(char), BUFFERSIZE, fp)) > 0)
	{
		printf("len = %d \n", len);
		if(send(socket, buff, len, 0) < 0)
		{
			perror("send file failed!\n");
			break;
		}
		printf("len:%d  file:%s", len, buff);
		bzero(buff, BUFFERSIZE+1);
	}
	fclose(fp);
	return 0;
}

static int recvFile(SOCKET socket)
{
	struct filePro{
		long int filesize;
		char fileName[MAXFILENAMESIZE];
	}file;
	char buff[BUFFERSIZE+1];
	
	char command[2][6] = { "begin", "donot" };
	//recv filePro from client
	int ret = recv(socket, (struct filePro*)&file, sizeof(struct filePro), 0);
	if(ret > 0)
	{
		printf("filename: %s  fileSize:%ld\n", file.fileName, file.filesize);
		char path[MAXFILENAMESIZE];
		bzero(path, MAXFILENAMESIZE);
		getLocalPath(file.fileName, path);
		printf("\t\t\t\t\tpath:%s\n", path);
		FILE* fp = fopen(path, "w+");
		if(fp == NULL)
		{
			//send info, unenable client upload file
			perror("create file error!\n");
			ret = send(socket, (char*)command[1], 6, 0);
			return -2;
		}
		
		//send info, enable client upload file
		printf("send info to client!\n");
		ret = send(socket, (char*)command[0], 6, 0);
		
		int sum = file.filesize;
		//此处没有对文件传输出错处理
		bzero(buff, BUFFERSIZE+1);
		while( sum > 0  && \
			(ret = recv(socket, (char*)buff, \
						(sum > BUFFERSIZE ? BUFFERSIZE:sum),\
						0)) > 0)
		{
			sum -= ret;
			printf("len: %d \n ", ret);
			printf("file:%s:end\n", buff);
			fwrite(buff, sizeof(char), strlen(buff), fp);
			bzero(buff, BUFFERSIZE);
		}
		printf("sum = %d  len = %ld\n", sum, ftell(fp));
		fclose(fp);
		//close(socket);
		return 0;
	}
	else
	{
		//send info, enable client upload file
		ret = send(socket, (char*)command[1], 6, 0);	
		return -1;
	}
}

static inline SOCKET creatConn()
{
/*
	int ret = 0;
	struct sockaddr_in sSockAddr;
	bzero(&sSockAddr, sizeof(struct sockaddr_in));
	sSockAddr.sin_port   = htons(SERVER_PORT);
	if((inet_aton(SERVER_IP, &sSockAddr.sin_addr)) == 0)
	{
		perror("IP error!\n");
		ret = -1;
	}
	
	int client = socket(AF_INET, SOCK_STREAM, 0);
	if(client < 0)
	{
		perror("create socket error!\n");
		ret = -2;
	}
	
	if(connect(client, (struct sockaddr*)&sSockAddr, \
				sizeof(struct sockaddr)) < 0)
	{
		perror("connect to server error!\n");
		ret = -3;
	}
	*/
}

static int sendFile(int socket, const char* desfileName, char* srcfileName)
{
	struct filePro{
		long int filesize;
		char fileName[MAXFILENAMESIZE];
	}file;
	
	//打开文件，顺便判断文件是否存在
	FILE *fp = fopen(srcfileName, "r");
	if(fp == NULL)
	{
		fprintf(stderr, "FILE:%s NOT FOUND!\n", srcfileName);
		return -1;
	}
	
	//获取文件大小
	long int size;
	GETFILESIZE(fp, size);
	printf("fileName:%s  size:%ld\n", srcfileName, size);
	
	//为消息（file）赋值
	bzero(&file, sizeof(struct filePro));
	strncpy(file.fileName, desfileName, strlen(desfileName));
	file.filesize = size; 
	
	//发送文件属性到服务器端（Web服务器），判断是否可以传输文件
	int ret = send(socket, (char*)&file, sizeof(struct filePro), 0);
	if(ret < 0)
	{
		fprintf(stderr, "send file property error!func: %s\n", __func__);
		return -2;
	}
	//等待结果反馈
	//如果不能发送文件，返回-3
	char command[6];
	bzero(command, 6);
	ret = recv(socket, command, sizeof(command), 0);
	if(strcmp(command, "begin") == 0)
	{
		printf("start to send file!\n");
	}
	else
	{
		printf("cannot send file!\n");
		return -3;
	}
	
	//发送文件正文
	int sum  = 0;
	char buff[BUFFERSIZE+1];
	bzero(buff, BUFFERSIZE+1);
	
	int len = 0;
	while((len = fread(buff, sizeof(char), BUFFERSIZE, fp)) > 0)
	{
		printf("len = %d \n", len);
		sum += len;
		if(send(socket, buff, len, BUFFERSIZE) < 0)
		{
			fprintf(stderr, "send file failed!\n");
			fclose(fp);
			return -4;
		}
		bzero(buff, BUFFERSIZE);
	}
	fclose(fp);
	printf("send file success!\n");
	return 0;
}

